package org.glandais.games.rebondrobots.board;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import org.glandais.games.rebondrobots.solver.RobotMove;

public class Board {

	private static final int CELL_SIZE = 16;

	private Cell[][] cells;

	private List<Goal> goals;

	private static enum WallShape {
		NW, NE, SW, SE;
	}

	private static WallShape[][] shapes = {

	{ WallShape.NW, WallShape.NE, WallShape.SW, WallShape.SE },

	{ WallShape.NE, WallShape.SW, WallShape.SE, WallShape.NW },

	{ WallShape.SW, WallShape.SE, WallShape.NW, WallShape.NE },

	{ WallShape.SE, WallShape.NW, WallShape.NE, WallShape.SW },

	};

	private static int[][][] positions = {

	{ { 1, 0 }, { 0, 2 }, { 4, 3 }, { 3, 5 } },

	{ { 0, 3 }, { 4, 0 }, { 5, 5 }, { 1, 5 } },

	{ { 4, 2 }, { 5, 5 }, { 0, 4 }, { 2, 0 } },

	{ { 5, 5 }, { 0, 1 }, { 1, 4 }, { 4, 2 } }

	};

	public Cell getCell(int x, int y) {
		return cells[x][y];
	}

	public void setCell(int x, int y, Cell cell) {
		cells[x][y] = cell;
	}

	public static Board createRandomBoard(Random r) {
		Board board = new Board();
		board.goals = new ArrayList<Goal>(16);
		createCells(board);
		addGoals(board, r);
		addWalls(board, r);
		return board;
	}

	public List<Goal> getGoals() {
		return goals;
	}

	private static void addWalls(Board board, Random r) {
		int i;
		do {
			i = r.nextInt(5);
		} while (board.cells[i + 2][1].getWest().isWall()
				|| board.cells[i + 2][1].getNorth().isWall()
				|| board.cells[i + 1][1].getNorth().isWall());
		board.cells[i + 2][0].getWest().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[i + 9][1].getWest().isWall()
				|| board.cells[i + 9][1].getNorth().isWall()
				|| board.cells[i + 8][1].getNorth().isWall());
		board.cells[i + 9][0].getWest().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[i + 2][14].getWest().isWall()
				|| board.cells[i + 2][14].getSouth().isWall()
				|| board.cells[i + 1][14].getSouth().isWall());
		board.cells[i + 2][15].getWest().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[i + 9][14].getWest().isWall()
				|| board.cells[i + 9][14].getSouth().isWall()
				|| board.cells[i + 8][14].getSouth().isWall());
		board.cells[i + 9][15].getWest().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[1][i + 2].getNorth().isWall()
				|| board.cells[1][i + 2].getWest().isWall()
				|| board.cells[1][i + 1].getWest().isWall());
		board.cells[0][i + 2].getNorth().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[2][i + 9].getNorth().isWall()
				|| board.cells[2][i + 9].getWest().isWall()
				|| board.cells[2][i + 8].getWest().isWall());
		board.cells[0][i + 9].getNorth().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[14][i + 2].getNorth().isWall()
				|| board.cells[14][i + 2].getEast().isWall()
				|| board.cells[14][i + 1].getEast().isWall());
		board.cells[15][i + 2].getNorth().setWall(true);

		do {
			i = r.nextInt(5);
		} while (board.cells[14][i + 2].getNorth().isWall()
				|| board.cells[14][i + 2].getEast().isWall()
				|| board.cells[14][i + 1].getEast().isWall());
		board.cells[15][i + 9].getNorth().setWall(true);
	}

	private static void addGoals(Board board, Random r) {
		List<WallShape[]> shapesList = Arrays.asList(shapes);
		List<int[][]> positionsList = Arrays.asList(positions);
		Collections.shuffle(shapesList, r);
		Collections.shuffle(positionsList, r);
		addGoals(board, 1, 1, shapesList.get(0), positionsList.get(0));
		addGoals(board, 9, 1, shapesList.get(1), positionsList.get(1));
		addGoals(board, 1, 9, shapesList.get(2), positionsList.get(2));
		addGoals(board, 9, 9, shapesList.get(3), positionsList.get(3));
	}

	private static void addGoals(Board board, int i, int j,
			WallShape[] wallShapes, int[][] positions) {
		for (int k = 0; k < wallShapes.length; k++) {
			WallShape wallShape = wallShapes[k];
			Cell cell = board.getCell(i + positions[k][0], j + positions[k][1]);
			Goal goal = new Goal();
			goal.setCell(cell);
			goal.setColor(RobotColor.values()[k]);
			cell.setGoal(goal);
			board.goals.add(goal);
			switch (wallShape) {
			case NW:
				cell.getNorth().setWall(true);
				cell.getWest().setWall(true);
				break;
			case NE:
				cell.getNorth().setWall(true);
				cell.getEast().setWall(true);
				break;
			case SW:
				cell.getSouth().setWall(true);
				cell.getWest().setWall(true);
				break;
			case SE:
				cell.getSouth().setWall(true);
				cell.getEast().setWall(true);
				break;
			default:
				break;
			}
		}
	}

	private static void createCells(Board board) {
		board.cells = new Cell[16][];
		for (int x = 0; x < 16; x++) {
			board.cells[x] = new Cell[16];
			for (int y = 0; y < 16; y++) {
				Cell cell = new Cell(x, y);
				board.cells[x][y] = cell;

				Border west;
				Border north;
				if (x > 0) {
					west = board.cells[x - 1][y].getEast();
				} else {
					west = new Border();
				}
				if (y > 0) {
					north = board.cells[x][y - 1].getSouth();
				} else {
					north = new Border();
				}
				Border east = new Border();
				Border south = new Border();

				setBorders(x, y, west, north, east, south);

				cell.setEast(east);
				cell.setSouth(south);
				cell.setWest(west);
				cell.setNorth(north);
			}
		}
	}

	private static void setBorders(int x, int y, Border west, Border north,
			Border east, Border south) {
		if (x == 0) {
			west.setWall(true);
		}
		if (x == 15) {
			east.setWall(true);
		}
		if (y == 0) {
			north.setWall(true);
		}
		if (y == 15) {
			south.setWall(true);
		}
		if (x == 7 && y == 7) {
			north.setWall(true);
			west.setWall(true);
		}
		if (x == 8 && y == 7) {
			north.setWall(true);
			east.setWall(true);
		}
		if (x == 7 && y == 8) {
			south.setWall(true);
			west.setWall(true);
		}
		if (x == 8 && y == 8) {
			south.setWall(true);
			east.setWall(true);
		}
	}

	public void saveToPNG(File file, Robots robots, RobotMove robotMove,
			Goal goal) {
		BufferedImage im = new BufferedImage(16 * CELL_SIZE, 16 * CELL_SIZE, 1);
		drawToImage(im, robots, goal, robotMove);
		try {
			ImageIO.write(im, "png", file);
		} catch (IOException e) {
			throw new RuntimeException("Failed to save image");
		}
	}

	private void drawToImage(BufferedImage im, Robots robots, Goal goal,
			RobotMove robotMove) {
		Graphics2D graphics = im.createGraphics();
		graphics.setColor(Color.WHITE);
		graphics.fillRect(0, 0, im.getWidth(), im.getHeight());

		graphics.setColor(Color.BLACK);
		graphics.setStroke(new BasicStroke(1.0f));
		for (int i = 0; i < 16; i++) {
			for (int j = 0; j < 16; j++) {
				int x = i * CELL_SIZE;
				int y = j * CELL_SIZE;
				Cell cell = getCell(i, j);
				if (cell.getNorth().isWall()) {
					graphics.drawLine(x, y, x + CELL_SIZE - 1, y);
				}
				if (cell.getWest().isWall()) {
					graphics.drawLine(x, y, x, y + CELL_SIZE - 1);
				}
				if (cell.getEast().isWall()) {
					graphics.drawLine(x + CELL_SIZE - 1, y, x + CELL_SIZE - 1,
							y + CELL_SIZE - 1);
				}
				if (cell.getSouth().isWall()) {
					graphics.drawLine(x, y + CELL_SIZE - 1, x + CELL_SIZE - 1,
							y + CELL_SIZE - 1);
				}
			}
		}

		Cell cell;
		int x;
		int y;

		// for (Goal goal : goals) {
		cell = goal.getCell();

		x = cell.getX() * CELL_SIZE + 2;
		y = cell.getY() * CELL_SIZE + 2;
		graphics.setColor(goal.getColor().getColor());
		graphics.fillOval(x, y, CELL_SIZE - 4, CELL_SIZE - 4);

		x = cell.getX() * CELL_SIZE + 1;
		y = cell.getY() * CELL_SIZE + 1;
		graphics.setColor(Color.BLACK);
		graphics.drawOval(x, y, CELL_SIZE - 2, CELL_SIZE - 2);

		/*
		 * x = cell.getX() * CELL_SIZE + 1; y = cell.getY() * CELL_SIZE + 1;
		 * graphics.setColor(goal.getColor().getColor()); //
		 * graphics.fillOval(x, y, 2, 2); graphics.drawLine(x, y, x + CELL_SIZE
		 * - 2, y + CELL_SIZE - 2); graphics.drawLine(x + CELL_SIZE - 2, y, x, y
		 * + CELL_SIZE - 2);
		 */
		// }

		RobotColor[] colors = RobotColor.values();
		for (RobotColor color : colors) {
			cell = robots.getPosition(color);
			x = cell.getX() * CELL_SIZE + 1;
			y = cell.getY() * CELL_SIZE + 1;
			graphics.setColor(color.getColor());
			graphics.fillOval(x, y, CELL_SIZE - 2, CELL_SIZE - 2);
		}

		if (robotMove != null && robotMove.getRobot() != null) {
			graphics.setColor(robotMove.getRobot().getColor());
			x = robotMove.getFromCell().getX() * CELL_SIZE + CELL_SIZE / 2;
			y = robotMove.getFromCell().getY() * CELL_SIZE + CELL_SIZE / 2;
			int x2 = robotMove.getToCell().getX() * CELL_SIZE + CELL_SIZE / 2;
			int y2 = robotMove.getToCell().getY() * CELL_SIZE + CELL_SIZE / 2;
			graphics.drawLine(x, y, x2, y2);
		}
	}
}
